use jsonwebtoken::{EncodingKey, Header};
use salvo::{Depot, Request};

use crate::config::app_config::CONFIG;
use crate::config::handler_config::{AdminType, MEMORY_CACHE};
use crate::entity::sys_menu::SysMenu;
use crate::entity::sys_role::SysRole;
use crate::service::get_login_user_role;
use crate::vo::sys_menu_vo::MenuVo;
use crate::{config::handler_config::Claims, entity::sys_user::SysUser, vo::login_vo::LoginReq};

pub struct LoginService {}

impl LoginService {
    pub async fn login(request: &mut Request, depot: &mut Depot) -> anyhow::Result<String> {
        let login_req = request.parse_json::<LoginReq>().await?;
        if login_req.user_name.is_none()
            || login_req.password.is_none()
            || login_req.org_id.is_none()
        {
            return Err(anyhow::anyhow!("params is none"));
        }
        let users = SysUser::query_user(login_req.user_name, login_req.org_id).await?;
        if let Some(user) = users {
            let role = SysRole::query_role(user.user_id, user.org_id).await?;

            if let Some(role) = role {
                let menus = Self::get_login_menu_by_role(role.role_id).await?;
                if let Some(menu) = menus {
                    if user.password.unwrap().eq(&login_req.password.unwrap()) {
                        let secret = &CONFIG.server.token.secret;
                        let token = jsonwebtoken::encode(
                            &Header::default(),
                            &Claims::new(
                                user.org_id,
                                AdminType::from(user.admin),
                                user.user_id,
                                user.user_name.clone(),
                                role.role_id,
                                Some(menu),
                            ),
                            &EncodingKey::from_secret(secret.as_bytes()),
                        )?;
                        let mut cache = MEMORY_CACHE.write().await;
                        let key = uuid::Uuid::new_v4().to_string();
                        cache.insert(key.clone(), token.clone());
                        return Ok(key);
                    }
                } else {
                    return Err(anyhow::anyhow!("menus is none "));
                }
            } else {
                return Err(anyhow::anyhow!("role is none "));
            }
        }
        Err(anyhow::anyhow!("login error "))
    }

    pub async fn get_login_menu(depot: &mut Depot) -> anyhow::Result<Option<Vec<MenuVo>>> {
        let role_id = get_login_user_role(depot).await?;
        SysMenu::query_menus(role_id).await
    }

    pub async fn get_login_menu_by_role(
        role_id: Option<i64>,
    ) -> anyhow::Result<Option<Vec<MenuVo>>> {
        SysMenu::query_menus(role_id).await
    }

    pub async fn logout(request: &mut Request, depot: &mut Depot) -> anyhow::Result<()> {
        let header = request.headers().get("authorization");
        if let Some(token) = header {
            let token_string = token.to_str().unwrap().replace("Bearer ", "");
            let mut cache = MEMORY_CACHE.write().await;
            cache.remove(&token_string);
            drop(cache);
        }

        Ok(())
    }
}
